package com.hhxy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hhxy.constants.SystemConstants;
import com.hhxy.handler.exception.CustomException;
import com.hhxy.mapper.ArticleMapper;
import com.hhxy.model.dto.AddArticleDto;
import com.hhxy.model.entity.Article;
import com.hhxy.model.entity.ArticleTag;
import com.hhxy.model.entity.Category;
import com.hhxy.model.vo.*;
import com.hhxy.service.ArticleService;
import com.hhxy.service.ArticleTagService;
import com.hhxy.service.CategoryService;
import com.hhxy.utils.BeanCopyUtils;
import com.hhxy.utils.redis.RedisCache;
import com.hhxy.utils.response.AppHttpCodeEnum;
import com.hhxy.utils.response.ResponseResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author ghp
 * @description 针对表【tb_article(文章表)】的数据库操作Service实现
 * @createDate 2023-04-25 11:58:34
 */
@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article>
        implements ArticleService {

    @Resource
    private CategoryService categoryService;

    @Resource
    private RedisCache redisCache;

    @Resource
    private ArticleTagService articleTagService;

    /**
     * 查询热门文章
     *
     * @return
     */
    @Override
    public ResponseResult hotArticleList() {
        // 查询热门文章（查询阅读量最多的前十篇文章）
        Page<Article> page = new Page<>(SystemConstants.ARTICLE_HOT_BEGIN, SystemConstants.ARTICLE_HOT_END);
        this.page(page, new LambdaQueryWrapper<Article>()
                .eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL)
                .orderByDesc(Article::getViewCount));
        List<Article> articleList = page.getRecords();

        // 封装Vo，然后响应给前台
        // 从redis中获取viewCount
        articleList.stream().map(article -> {
            Integer viewCount = redisCache.getCacheMapValue(SystemConstants.ARTICLE_VIEW_COUNT, article.getId().toString());
            article.setViewCount(viewCount.longValue());
            return article;
        }).collect(Collectors.toList());

        List<HotArticleVo> hotArticleVoList = BeanCopyUtils.copyBeanList(articleList, HotArticleVo.class);
        return ResponseResult.okResult(hotArticleVoList);
    }

    /**
     * 分页条件查询文章
     *
     * @param pageNum
     * @param pageSize
     * @param categoryId
     * @return
     */
    @Override
    public ResponseResult articleList(Integer pageNum, Integer pageSize, Long categoryId) {
        //  分页条件查询文章（文章是已发布的，并且根据isTop字段进行降序排序）
        Page<Article> page = new Page<>(pageNum, pageSize);
        this.page(page, new LambdaQueryWrapper<Article>()
                .eq(Objects.nonNull(categoryId) && categoryId > 0, Article::getCategoryId, categoryId)
                .eq(Article::getStatus, SystemConstants.ARTICLE_STATUS_NORMAL)
                .orderByDesc(Article::getIsTop));
        List<Article> articleList = page.getRecords();

        // 封装VO（需要单独将分类名封装到VO中）
        // 方式一：
        articleList.stream().map(article -> {
            String categoryName = categoryService.getById(article.getCategoryId()).getName();
            article.setCategoryName(categoryName);
            return categoryName;
        });
        // 方式二：
//        articleList.stream().map(article -> {
//            String categoryName = categoryService.getById(article.getCategoryId()).getName();
//            return article.setCategoryName(categoryName);
//        });
        // 方式三：
//        for (Article article : articleList) {
//            Category category = categoryService.getById(article.getCategoryId());
//            article.setCategoryName(category.getName());
//        }

        // 封装VO，响应给前台

        List<ArticleListVo> articleListVos = BeanCopyUtils.copyBeanList(page.getRecords(), ArticleListVo.class);
        PageVo pageVo = new PageVo(articleListVos, page.getTotal());
        return ResponseResult.okResult(pageVo);
    }

    /**
     * 根据id查看文章详情
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult getArticleDetail(Long id) {
        // 根据id查询文章
        Article article = this.getById(id);

        // 从redis中获取viewCount
        Integer viewCount = redisCache.getCacheMapValue(SystemConstants.ARTICLE_VIEW_COUNT, id.toString());
        article.setViewCount(viewCount.longValue());

        // 封装VO（需要得到文章的分类名），响应数据
        ArticleDetailVo articleDetailVo = BeanCopyUtils.copyBean(article, ArticleDetailVo.class);
        // 根据分类id查询分类名
        Long categoryId = articleDetailVo.getCategoryId();
        Category category = categoryService.getById(categoryId);
        if (category != null) {
            // 防止文章没有分类而导致的空指针异常
            articleDetailVo.setCategoryName(category.getName());
        }
        return ResponseResult.okResult(articleDetailVo);
    }

    /**
     * 根据文章id更新文章访问量
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult updateViewCount(Long id) {
        // 更新存储在redis中的文章访问量
        redisCache.incrementCacheMapValue(SystemConstants.ARTICLE_VIEW_COUNT, id.toString(), 1);
        return ResponseResult.okResult();
    }

    /**
     * 分页查询文章
     *
     * @param pageNum
     * @param pageSize
     * @param title
     * @param summary
     * @return
     */
    @Override
    public ResponseResult list(Integer pageNum, Integer pageSize, String title, String summary) {
        // 分页查询文章
        Page<Article> page = new Page<>(pageNum, pageSize);
        this.page(page, new LambdaQueryWrapper<Article>()
                .like(StringUtils.hasText(title), Article::getTitle, title)
                .like(StringUtils.hasText(summary), Article::getSummary, summary)
                .orderByDesc(Article::getUpdateTime));
        // 封装VO
        List<ArticlePageInfoVo> articlePageInfoVoList = BeanCopyUtils.copyBeanList(page.getRecords(), ArticlePageInfoVo.class);
        return ResponseResult.okResult(new PageVo<ArticlePageInfoVo>(articlePageInfoVoList, page.getTotal()));
    }

    /**
     * 新增文章
     *
     * @param addArticleDto
     * @return
     */
    @Override
    @Transactional
    public ResponseResult add(AddArticleDto addArticleDto) {
        // 存储博客数据（操作tb_article表）
        Article article = BeanCopyUtils.copyBean(addArticleDto, Article.class);
        boolean flag = this.save(article);
        if (!flag) {
            throw new CustomException(AppHttpCodeEnum.SERVICE_INSERT_ERROR);
        }

        // 存储文章与标签关系（操作tb_article_tag表）
        // 构建文章与标签的关系
        List<ArticleTag> articleTags = addArticleDto.getTags().stream()
                .map(tagId -> new ArticleTag(article.getId(), tagId))
                .collect(Collectors.toList());
        // 添加 博客和标签的关系
        flag = articleTagService.saveBatch(articleTags);
        if (!flag) {
            throw new CustomException(AppHttpCodeEnum.SERVICE_INSERT_ERROR);
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS_INSERT);
    }

    /**
     * 根据id删除文章
     *
     * @param ids
     * @return
     */
    @Override
    @Transactional
    public ResponseResult deleteArticleByIds(Long[] ids) {
        // 删除文章（操作tb_article表）
        boolean flag = this.removeByIds(Arrays.asList(ids));
        if (!flag) {
            throw new CustomException(AppHttpCodeEnum.SERVICE_DELETE_ERROR);
        }
        // 删除文章和标签的关系（操作tb_article_tag表）
        for (Long id : ids) {
            flag = articleTagService.remove(new LambdaQueryWrapper<ArticleTag>()
                    .eq(ArticleTag::getArticleId, id));
            if (!flag) {
                throw new CustomException(AppHttpCodeEnum.SERVICE_DELETE_ERROR);
            }
        }
        return ResponseResult.errorResult(AppHttpCodeEnum.SUCCESS_DELETE);
    }

    /**
     * 根据id查询文章信息
     *
     * @param id
     * @return
     */
    @Override
    public ResponseResult getArticleById(Long id) {
        // 查询文章信息
        Article article = this.getById(id);
        // 查询文章对应的标签信息
        List<ArticleTag> articleTagList = articleTagService.list(new LambdaQueryWrapper<ArticleTag>()
                .eq(ArticleTag::getArticleId, id));
        List<Long> tagIds = articleTagList.stream().map(articleTag -> articleTag.getTagId())
                .collect(Collectors.toList());
        // 封装VO
        ArticleEditInfoVo articleEditInfoVo = BeanCopyUtils.copyBean(article, ArticleEditInfoVo.class);
        articleEditInfoVo.setTags(tagIds);
        return ResponseResult.okResult(articleEditInfoVo);
    }

    /**
     * 根据id修改文章信息
     *
     * @param addArticleDto
     * @return
     */
    @Override
    @Transactional
    public ResponseResult updateArticleById(AddArticleDto addArticleDto) {
        Article article = BeanCopyUtils.copyBean(addArticleDto, Article.class);
        // 修改文章信息（操作tb_article表）
        boolean flag = this.updateById(article);
        if (!flag) {
            throw new CustomException(AppHttpCodeEnum.SERVICE_UPDATE_ERROR);
        }

        // 修改文章标签关系（先删除原有的关系，然后新增关系，操作tb_article_tag表）
        // 删除原有的关系（注意不能直接使用ById方法）
        int count = articleTagService.count(new LambdaQueryWrapper<ArticleTag>()
                .eq(ArticleTag::getArticleId, article.getId()));
        if (count > 0) {
            // 只有当存在关系时才删除（防止没有删除remove方法返回false导致系统抛异常）
            flag = articleTagService.remove(new LambdaQueryWrapper<ArticleTag>()
                    .eq(ArticleTag::getArticleId, article.getId()));
            if (!flag) {
                throw new CustomException(AppHttpCodeEnum.SERVICE_UPDATE_ERROR);
            }
        }
        // 新增 文章-标签 关系
        for (Long tagId : addArticleDto.getTags()) {
            flag = articleTagService.save(new ArticleTag(article.getId(), tagId));
            if (!flag) {
                throw new CustomException(AppHttpCodeEnum.SERVICE_UPDATE_ERROR);
            }
        }

        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS_UPDATE);
    }
}




